import os
from PySide6 import QtCore
from PySide6.QtCore import (
    Qt,
    QPoint,
    Signal
)
from PySide6.QtGui import (
    QColor,
    QResizeEvent,
    QPainter,
    QFontMetrics,
    QIcon,
    QPixmap,
    QPen,
    QFont,
    QKeySequence, 
    QKeyEvent,
)
from PySide6.QtWidgets import (
    QWidget,
    QColorDialog,
    QLineEdit,
    QTableWidgetItem,
    QDialog,
    QAbstractItemView,
    QSizePolicy,
    QPushButton,
    QButtonGroup,
    QVBoxLayout,
    QHBoxLayout,
)

# 导入枚举和配置
from gui.schemas.enumeration import AnnotationStatus, CommentStatus, UserRole
from gui.ui.UI_annotator_window import Ui_Form_annotator
from gui.schemas.configs import configs

# 导入网络请求和异常处理
import requests
import traceback

# 导入标注相关模块
from gui.utils.annotation_manager import CommandType, ResponseStatus
from gui.window.annotator.graphics_items import (
    ResizableGraphicsRectItem, 
    ResizableGraphicsPolygonItem, 
    ResizableGraphicsQuadrilateralItem
)

# 在文件顶部添加
class Constants:
    # 表格列宽设置
    TABLE_FILENAME_WIDTH = 120
    TABLE_STATUS_WIDTH = 20
    TABLE_CHECK_WIDTH = 20
    # 图标尺寸
    SHAPE_ICON_SIZE = 64
    # 分割器比例
    SPLITTER_RATIO = 10  # 1/10比例
    # 字体大小
    TABLE_ITEM_FONT_SIZE = 7
    # 样式表常量
    TABLE_SELECTED_STYLE = """
        QTableWidget::item:selected {
            background-color: #a6c9e2;
            color: black;
        }
        QTableWidget::item:selected:!active {
            background-color: #a6c9e2;
            color: black;
        }
    """
    
def create_shape_icon(shape, size=Constants.SHAPE_ICON_SIZE):
    """创建形状图标，用于形状选择按钮"""
    pixmap = QPixmap(size, size)
    pixmap.fill(Qt.transparent)
    
    painter = QPainter(pixmap)
    painter.setRenderHint(QPainter.Antialiasing, True)
    pen = QPen(Qt.black, 3)
    painter.setPen(pen)
    painter.setBrush(Qt.white)
    
    # 使用字典表达式简化条件判断
    shape_drawers = {
        "rectangle": lambda: painter.drawRect(1, 10, size - 20, size - 20),
        "quadrilateral": lambda: painter.drawPolygon([
            QPoint(1, 8), QPoint(size - 8, 10),
            QPoint(size - 4, size - 10), QPoint(20, size - 15)
        ]),
        "polygon": lambda: painter.drawPolygon([
            QPoint(size -25, 10), QPoint(8, 20), QPoint(15, size - 10),
            QPoint(size - 30, size - 10), QPoint(size - 20, size - 10),
            QPoint(size - 1, 10)
        ])
    }
    
    # 调用对应的绘制函数，默认绘制矩形
    shape_drawers.get(shape, shape_drawers["rectangle"])()
    painter.end()
    return pixmap

class CategoryItemWidget(QWidget):
    update_signal = Signal(int, dict)

    """分类标签项控件，用于显示和编辑分类信息"""
    def __init__(self, index=0, name="", color="#FF0000", shortcut="", parent=None):
        super().__init__(parent)
        self.index = index
        self.main_layout = QHBoxLayout(self)
        self.main_layout.setContentsMargins(0, 0, 0, 0)  # 清除布局边距
        
        # 初始化UI组件
        self._init_ui(name, color, shortcut)
        
        # 设置布局和大小策略
        self.setLayout(self.main_layout)
        self.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Preferred)
        
    def _init_ui(self, name, color, shortcut):
        """初始化UI组件"""
        # 名称输入框
        self.name_edit = QLineEdit(name)
        self.name_edit.textChanged.connect(self.textChanged)
        self.name_edit.setReadOnly(True)  # 初始为只读
        self.name_edit.setPlaceholderText("分类名称")
        
        # 颜色选择按钮
        self.color_btn = QPushButton("__")  # 显示为两个下划线占位
        self.color_btn.setProperty("color", color)
        self.color_btn.clicked.connect(self.select_color)  # 绑定颜色选择事件
        
        # 快捷键输入框
        self.shortcut_edit = QLineEdit()
        self.shortcut_edit.textChanged.connect(self.textChanged)
        self.shortcut_edit.setMaximumWidth(50)
        self.shortcut_edit.setPlaceholderText("按快捷键...")
        self.shortcut_edit.setReadOnly(True)
        self.shortcut_edit.setFocusPolicy(Qt.StrongFocus)  # 确保能接收焦点
        self.shortcut_edit.setText(shortcut)
        
        # 设置初始颜色
        self.set_color(color)
        
        # 添加控件到布局
        self.main_layout.addWidget(self.color_btn)
        self.main_layout.addWidget(self.name_edit)
        self.main_layout.addWidget(self.shortcut_edit)
        
    def textChanged(self, text):
        """文本变化时调整输入框宽度，确保内容完整显示"""
        line_edit = self.sender()
        if text is None:
            text = line_edit.text()
            
        # 计算文本宽度（添加额外空格优化显示）
        text = text + (" " * max(1, len(text) // 2))
        font_metrics = QFontMetrics(line_edit.font())
        text_width = font_metrics.horizontalAdvance(text)
        
        # 处理占位符文本的宽度计算
        if not text and line_edit.placeholderText():
            text_width = font_metrics.horizontalAdvance(line_edit.placeholderText())
            
        # 计算最终宽度（包含内边距）
        left, right = line_edit.contentsMargins().left(), line_edit.contentsMargins().right()
        desired_width = text_width + left + right
        
        # 设置最小宽度
        line_edit.setMinimumWidth(desired_width)
        
    def select_color(self):
        """打开颜色选择对话框并更新按钮颜色"""
        # 打开颜色选择对话框，初始颜色为当前按钮颜色
        color = QColorDialog.getColor(QColor(self.color_btn.property("color")), self)
        if color.isValid():  # 检查用户是否选择了有效颜色
            self.set_color(color.name())
            
    def set_color(self, color):
        """设置按钮颜色并更新样式"""
        # 使用样式表设置按钮背景色
        self.color_btn.setStyleSheet(f"background-color: {color}; border-radius: 3px;")
        self.color_btn.setProperty("color", color)
        self.update_signal.emit(self.index, self.get_data())
        
    def get_data(self):
        """获取分类数据（名称、颜色、快捷键）"""
        return {
            "name": self.name_edit.text(),
            "color": self.color_btn.property("color"),
            "shortcut": self.shortcut_edit.text()
        }
        
    def keyPressEvent(self, event: QKeyEvent):
        """捕获键盘事件，解析并设置快捷键组合"""
        # 只处理快捷键输入框获得焦点的情况
        if not self.shortcut_edit.hasFocus():
            super().keyPressEvent(event)
            return
            
        # 忽略回车键，避免意外行为
        if event.key() in (Qt.Key_Return, Qt.Key_Enter):
            event.accept()
            return
            
        # 解析修饰键（Ctrl、Shift、Alt、Meta）
        modifiers = []
        if event.modifiers() & Qt.ControlModifier:
            modifiers.append("Ctrl")
        if event.modifiers() & Qt.ShiftModifier:
            modifiers.append("Shift")
        if event.modifiers() & Qt.AltModifier:
            modifiers.append("Alt")
        if event.modifiers() & Qt.MetaModifier:  # Windows:Win键, Mac:Command键
            modifiers.append("Meta")
            
        # 过滤纯修饰键（仅按修饰键不设置快捷键）
        key = event.key()
        if key in (Qt.Key_Control, Qt.Key_Shift, Qt.Key_Alt, Qt.Key_Meta):
            event.accept()
            return
            
        # 转换键值为字符串并移除可能的&符号
        key_str = QKeySequence(key).toString().replace('&', '')
        
        # 组合快捷键字符串（如"Ctrl+Shift+A"）
        if modifiers:
            shortcut_str = "+".join(modifiers) + "+" + key_str
        else:
            shortcut_str = key_str
        if self.shortcut_edit.text() == shortcut_str:
            return
        # 更新输入框显示
        self.shortcut_edit.setText(shortcut_str)
        self.update_signal.emit(self.index, self.get_data())

        event.accept()


class Job_Tab_Annotator(Ui_Form_annotator, QDialog):
    """
    标注工作标签页组件，负责标注界面的主要逻辑和交互
    """
    
    # 信号：文件头数据变更 (行号, 新数据)
    file_headerDataChanged = Signal(int, dict)
    update_annotations = Signal(dict)
    
    
    def __init__(self, parent=None, tab_info={}):
        super(Job_Tab_Annotator, self).__init__()
        
        # 初始化基本属性
        self.setupUi(self)
        self.main_panel = parent  # 父窗口引用
        self.tab_info = tab_info    # 标签页信息字典
        self.filetype = tab_info.get("type", "图片")  # 文件类型，默认为图片
        self.user_metadata = tab_info.get("user_metadata", {})  # 用户元数据
        
        # 当前文件信息初始化
        self.current_filename = self.user_metadata.get("job", {}).get("current_filename", "")
        self.current_id = int(self.user_metadata.get("job", {}).get("current_id", 0))
        
        # 标签页索引
        self.index = self.main_panel.tabWidget_anno.indexOf(self)
        
        # 数据管理器和本地数据目录
        self.data_manager = parent.data_manager
        self.data_manager.signals.command_result.connect(self.on_command_result)  # 绑定命令结果回调
        self.data_manager.signals.annotation_data.connect(self.on_annotation_data)  # 绑定命令结果回调
        self.local_data_dir = self.data_manager.local_data_dir
        
        # 初始化标注器界面
        self.set_annotator()
        
    def on_command_result(self, cmd_type, result, status):
        """
        命令执行结果回调处理函数
        
        Args:
            cmd_type: 命令类型（来自CommandType枚举）
            result: 命令执行结果数据
            status: 执行状态（成功/失败）
        """
        if status == ResponseStatus.SUCCESS:
            if cmd_type == CommandType.SAVE_FILE_ANNOTATION:
                # 保存标注结果成功，可在此添加后续处理（如提示用户）
                pass
            elif cmd_type == CommandType.GET_FILES_DATA:
                # 获取文件数据成功，刷新文件列表
                file_datas = result.get("file_datas", {})
                self.refresh_files(file_datas)
        elif status == ResponseStatus.FAILURE:
            # 命令执行失败，打印错误信息
            print(f"命令执行失败: {cmd_type}, 错误: {result.get('error', '未知错误')}")
            
    def on_annotation_data(self, job_id, file_annotation):
        """
        新分配标注数据更新回调处理函数
        
        Args:
            job_id: 任务ID
            file_annotation: 文件标注数据
        """
        if job_id != int(self.tab_info["id"]):
            return
        # 移除已存在的文件（避免重复添加）
        row_count = self.tableWidget_files.rowCount()
        for row in range(row_count):
            header_item = self.tableWidget_files.verticalHeaderItem(row)
            if header_item:
                file_data = header_item.data(Qt.ItemDataRole.UserRole)
                filename = file_data.get("filename", "")
                if filename == file_annotation["filename"]: # 新分配的文件
                    return 
        self.add_file_to_table(filename, file_annotation, "Redis")
        

    def annotations_changed_file_data(self, ):
        """
        标注内容变更时更新文件数据
        
        当标注图形发生变化（如添加、修改、删除）时，
        收集所有图形数据并更新到当前文件数据中
        """
        # 获取选中行（当前正在处理的文件）
        selected_rows = [row.row() for row in self.tableWidget_files.selectionModel().selectedRows()]
        if not selected_rows:
            return
            
        row = selected_rows[0]
        # 获取文件数据（存储在表头的UserRole中）
        header_item = self.tableWidget_files.verticalHeaderItem(row)
        file_data = header_item.data(Qt.ItemDataRole.UserRole) if header_item else {}
        
        # 初始化区域和评论数据
        regions = []
        reviews = {
            "comment": "", 
            "regions": [], 
            "id": file_data.get("id")
        }
        
        # 收集所有图形项数据
        for child_item in self.graphicsView_image_display.get_image_child_items():
            # 根据图形类型处理不同的形状属性
            if isinstance(child_item, ResizableGraphicsRectItem):
                # 处理矩形
                shape_attrs = {
                    "name": "rect",
                    "x": child_item.rect().x(),
                    "y": child_item.rect().y(),
                    "width": child_item.rect().width(),
                    "height": child_item.rect().height()
                }
            elif isinstance(child_item, ResizableGraphicsQuadrilateralItem):
                # 处理四边形
                polygon = child_item.polygon().toList()
                shape_attrs = {
                    "name": "polygon",
                    "all_points_x": [p.x() for p in polygon],
                    "all_points_y": [p.y() for p in polygon]
                }
            elif isinstance(child_item, ResizableGraphicsPolygonItem):
                # 处理多边形
                polygon = child_item.polygon().toList()
                shape_attrs = {
                    "name": "polygon",
                    "all_points_x": [p.x() for p in polygon],
                    "all_points_y": [p.y() for p in polygon]
                }
            else:
                continue  # 跳过未知形状
            
            # 收集区域属性
            region = {
                "shape_attributes": shape_attrs,
                "region_attributes": {
                    "obj": child_item.object,
                    "worker": self.main_panel.userinfo["username"],
                    "transliterate": child_item.transliterate,
                    "updatetime": child_item.updatetime.strftime("%Y-%m-%d %H:%M:%S"),
                },
            }
            
            # 收集评论信息
            region_comment = {
                "comment": child_item.comment,
                "updatetime": child_item.updatetime.strftime("%Y-%m-%d %H:%M:%S"),
            }
            
            regions.append(region)
            reviews["regions"].append(region_comment)
        
        # 更新文件数据中的区域信息
        file_data["annotation"] = regions
        
        # 根据角色限制数据修改权限
        if UserRole.ANNOTATOR in self.main_panel.userinfo["roles"]:
            reviews = None  # 标注人员不能修改评论
        elif UserRole.EMEND in self.main_panel.userinfo["roles"]:
            file_data = None  # 审核人员不能修改标注
            
        # 保存更新后的数据
        self.update_file_data(row, file_data, reviews)
        
    def set_annotator(self):
        """初始化标注器界面组件，包括撤销栈、文件表格、形状按钮和分类布局"""
        # 设置撤销栈视图
        self.undoView_records.setStack(self.graphicsView_image_display.undo_stack)
        self.undoView_records.setEmptyLabel("操作历史")
        # 绑定撤销栈变化事件，用于自动保存标注变更
        self.graphicsView_image_display.updateAnnotationsChanged.connect(self.annotations_changed_file_data)
        
        # 初始化文件列表表格
        self._init_file_table()
        
        # 根据文件类型设置界面元素可见性
        if self.filetype == "图片":
            self.frame_class_group.setVisible(False)
            
        # 初始化形状选择按钮组
        self._init_shape_buttons()
        
        # 初始化分类标签布局
        self._init_category_layout()
        # 设置标签属性
        self.set_shape_properties(self.tab_info.get("attributes",{}))
    
    def set_shape_properties(self, shape_properties):   
         if shape_properties.get("label_type") == "区域注释":
            self.graphicsView_image_display.set_shape_properties(shape_properties)
            pass
         else: # 文件注释
            """文件注释的时候应该通过显示多类别文件方法显示，选择图片右击修改类别（相当于移动文件袋对应类别），ocr则是图片下方都有输入框"""
            pass
    
    def _init_file_table(self):
        """初始化文件列表表格，设置列、样式和选择行为"""
        self.tableWidget_files.setColumnCount(3)
        # 设置表头
        self.tableWidget_files.setHorizontalHeaderItem(0, QTableWidgetItem("文件名"))
        self.tableWidget_files.setHorizontalHeaderItem(1, QTableWidgetItem("状态"))
        self.tableWidget_files.setHorizontalHeaderItem(2, QTableWidgetItem("检查"))
        
        # 设置列宽
        self.tableWidget_files.setColumnWidth(0, 120)
        self.tableWidget_files.setColumnWidth(1, 25)
        self.tableWidget_files.setColumnWidth(2, 20)
        
        # 设置表格属性
        self.tableWidget_files.setEditTriggers(QAbstractItemView.NoEditTriggers)  # 不可编辑
        self.tableWidget_files.setSelectionBehavior(QAbstractItemView.SelectRows)  # 选择整行
        self.tableWidget_files.setSortingEnabled(False)  # 禁用排序（避免数据混乱）
        self.tableWidget_files.setWordWrap(False)
        self.tableWidget_files.setRowCount(0)  # 初始行数为0
        self.tableWidget_files.horizontalHeader().setStretchLastSection(True)  # 最后一列自适应宽度
        self.tableWidget_files.setAlternatingRowColors(True)  # 交替行颜色，增强可读性
        
        # 设置选中样式
        self.tableWidget_files.setStyleSheet("""
            QTableWidget::item:selected {
                background-color: #a6c9e2;
                color: black;
            }
            QTableWidget::item:selected:!active {
                background-color: #a6c9e2;
                color: black;
            }
        """)
        
        # 绑定选择事件，当选中行变化时触发
        self.tableWidget_files.itemSelectionChanged.connect(self.file_selected)
        
    def _init_shape_buttons(self):
        """初始化形状选择按钮组，包括矩形、四边形和多边形按钮"""
        self.buttongroup_shape = QButtonGroup(self)
        
        # 矩形按钮
        self.pushButton_shape_rectangle = QPushButton()
        self.pushButton_shape_rectangle.setCheckable(True)
        self.pushButton_shape_rectangle.setIcon(QIcon(create_shape_icon("rectangle")))
        self.pushButton_shape_rectangle.clicked.connect(
            lambda: self.graphicsView_image_display.set_tool('rectangle')
        )
        
        # 四边形按钮
        self.pushButton_shape_quadrilateral = QPushButton()
        self.pushButton_shape_quadrilateral.setCheckable(True)
        self.pushButton_shape_quadrilateral.setIcon(QIcon(create_shape_icon("quadrilateral")))
        self.pushButton_shape_quadrilateral.clicked.connect(
            lambda: self.graphicsView_image_display.set_tool('quadrilateral')
        )
        
        # 多边形按钮
        self.pushButton_shape_polygon = QPushButton()
        self.pushButton_shape_polygon.setCheckable(True)
        self.pushButton_shape_polygon.setIcon(QIcon(create_shape_icon("polygon")))
        self.pushButton_shape_polygon.clicked.connect(
            lambda: self.graphicsView_image_display.set_tool('polygon')
        )
        
        # 添加到按钮组（互斥选择）
        self.buttongroup_shape.addButton(self.pushButton_shape_rectangle)
        self.buttongroup_shape.addButton(self.pushButton_shape_quadrilateral)
        self.buttongroup_shape.addButton(self.pushButton_shape_polygon)
        
        # 添加到布局
        self.horizontalLayout_groupBox_shape.addWidget(self.pushButton_shape_rectangle)
        self.horizontalLayout_groupBox_shape.addWidget(self.pushButton_shape_quadrilateral)
        self.horizontalLayout_groupBox_shape.addWidget(self.pushButton_shape_polygon)
        
        # 默认选中矩形工具
        self.pushButton_shape_rectangle.click()
        
    def _init_category_layout(self):
        """初始化分类标签布局，加载分类数据并创建分类项控件"""
        self.categories_layout = QVBoxLayout(self.tableView_label_options)
        self.categories_layout.setAlignment(Qt.AlignTop)  # 顶部对齐
        self.categories_layout.setSpacing(5)  # 控件间距
        
        # 加载分类数据
        attributes = self.tab_info.get("attributes", {})
        self.categories = []
        if attributes.get("annotation_method") == "分类":
            self.categories = attributes.get("categories", [])
            # 为每个分类创建控件
            for index, category in enumerate(self.categories):
                item_widget = CategoryItemWidget(
                    index=index,
                    name=category.get("name", ""),
                    color=category.get("color", "#FF0000"),
                    shortcut=category.get("shortcut", "")
                )
                item_widget.update_signal.connect(self.update_category)
                self.categories_layout.addWidget(item_widget)
        else: # OCR转写或描述不需要
            pass
        # 设置大小策略
        self.tableView_label_options.setSizePolicy(
            QSizePolicy.Policy.Expanding, 
            QSizePolicy.Policy.Preferred
        )
    
    def update_category(self, index, data):
        """更新分类数据"""
        self.categories[index].update(data)
        self.tab_info["attributes"]["categories"][index].update(data)
        self.set_shape_properties(self.tab_info.get("attributes",{}))
        # self.data_manager.enqueue_command(CommandType.SAVE_JOB, {"job_info": self.tab_info})




    def resizeEvent(self, event: QResizeEvent):
        """窗口大小改变时调整分割器布局"""
        size = self.splitter_three_panel.size()
        # 根据分割器方向计算总尺寸
        total_size = size.width() if self.splitter_three_panel.orientation() == Qt.Horizontal else size.height()
        half_size = total_size // 10  # 1/10的总尺寸
        # 设置三个面板的大小比例为1:8:1
        self.splitter_three_panel.setSizes([half_size, half_size * 8, half_size])
        super().resizeEvent(event)

    def get_files_data(self):
        """获取文件数据，通过数据管理器发送命令"""
        params = {"job_data": self.tab_info}
        self.data_manager.enqueue_command(CommandType.GET_FILES_DATA, params)

    def refresh_files(self, files: dict):
        """
        刷新文件列表，添加新文件并保持已有文件
        
        Args:
            files: 文件数据字典 {文件名: 文件信息}
        """
        file_keys = list(files.keys())
        file_keys.sort()  # 排序文件名
        
        # 移除已存在的文件（避免重复添加）
        row_count = self.tableWidget_files.rowCount()
        for row in range(row_count):
            header_item = self.tableWidget_files.verticalHeaderItem(row)
            if header_item:
                file_data = header_item.data(Qt.ItemDataRole.UserRole)
                filename = file_data.get("filename", "")
                if filename in file_keys:
                    file_keys.remove(filename)
                    
        # 添加新文件到表格
        for filename in file_keys:
            file_data = files[filename]
            if file_data:  # 只添加有效数据
                self.add_file_to_table(filename, file_data, "Redis")
                
        # 如果没有选中行，默认选中当前文件
        if self.tableWidget_files.currentRow() < 0:
            self.tableWidget_files.selectRow(self.current_id)

    def add_file_to_table(self, filename, file_data, source):
        """
        添加文件到表格中
        
        Args:
            filename: 文件名
            file_data: 文件详细信息
            source: 数据来源（如"Redis"）
        """
        # 构建文件路径
        file_data["filepath"] = os.path.join(
            self.local_data_dir, 
            self.tab_info.get("id"), 
            filename
        )
        # 创建表格项并存储文件数据
        widget_item = QTableWidgetItem(os.path.basename(filename))
        widget_item.setData(Qt.ItemDataRole.UserRole, file_data)
        widget_item.setFont(QFont('Sans Serif', 7))  # 小字体显示
        widget_item.setToolTip(file_data["filepath"])  # 显示完整路径提示
        
        # 创建行标题项并存储文件数据
        vertical_header = QTableWidgetItem()
        vertical_header.setData(Qt.ItemDataRole.UserRole, file_data)
        
        # 添加到表格
        self.tableWidget_files.insertRow(self.tableWidget_files.rowCount())
        current_id = self.tableWidget_files.rowCount() - 1  # 新行索引
        vertical_header.setText(str(current_id))  # 行标题显示索引
        self.tableWidget_files.setVerticalHeaderItem(current_id, vertical_header)
        self.tableWidget_files.setItem(current_id, 0, widget_item)
        
        # 更新状态显示
        self.update_file_ui_status(current_id, file_data)
        
        # 更新当前文件信息（如果是当前文件）
        if filename == self.current_filename:
            self.current_id = current_id
            self.current_filename = filename

    def update_file_data(self, row, file_data, reviews):
        """
        更新文件数据并保存到数据源
        
        Args:
            row: 行索引
            file_data: 更新后的文件数据
            reviews: 评论数据（可为None）
        """
        if file_data:
            job_id = self.tab_info.get("id")
            job_name = self.tab_info.get("name")
            filename = file_data.get("filename")
            annotation = file_data.get("annotation", [])
            status = file_data.get("status", "")
            # 更新标注状态：如果有区域且状态为"已查看"，则改为"已完成"
            if len(annotation) > 0 and status in [AnnotationStatus.VIEWED]:
                file_data["status"] = AnnotationStatus.COMPLETED
                self.update_file_ui_status(row, file_data)
                
            # 更新表头数据
            header_item = self.tableWidget_files.verticalHeaderItem(row)
            if header_item:
                header_item.setData(Qt.ItemDataRole.UserRole, file_data)
            
        # 发送数据变更信号
        self.file_headerDataChanged.emit(row, file_data)
        # 保存标注数据到数据源
        self.data_manager.enqueue_command(
            CommandType.SAVE_FILE_ANNOTATION, 
            {
                "job_id": job_id, 
                "file_annotation": file_data, 
                "reviews": reviews
            }
        )

    def file_selected(self):
        """文件选择变更处理：加载选中文件的图片和标注数据"""
        selected_rows = [row.row() for row in self.tableWidget_files.selectionModel().selectedRows()]
        if not selected_rows:
            return
            
        try:
            row = selected_rows[0]
            header_item = self.tableWidget_files.verticalHeaderItem(row)
            file_data = header_item.data(Qt.ItemDataRole.UserRole) if header_item else {}
            
            # 更新当前文件信息
            self.current_id = row
            self.current_filename = file_data.get("filename", "")
            self.file_data = file_data
            
            # # 根据角色选择不同的文件路径
            # if UserRole.ANNOTATOR in self.main_panel.userinfo["roles"]:
            #     # 标注员使用本地路径（替换斜杠适应Windows）
            #     print(file_data)
            #     filepath = file_data.get("filepath", "").replace("/", "\\")
            # else:
            # 审核员使用网络路径
            filepath = f'{configs.IMAGES_DOWNLOAD_BASE_URL}?file_path={file_data.get("path", "")}'
                
            # 在图形视图中显示图片
            self.graphicsView_image_display.set_image(filepath)
            
            # 更新文件状态为"已浏览"（如果是未处理状态）
            current_status = file_data.get("status", "")
            if current_status in [AnnotationStatus.UNASSIGNED, AnnotationStatus.ASSIGNED]:
                file_data["status"] = AnnotationStatus.VIEWED
                reviews = file_data.pop("reviews", [])
                self.update_file_data(row, file_data, reviews)
                self.update_file_ui_status(self.current_id, file_data)
                
            # 加载该文件的标注数据
            self.load_annannotation(self.current_id, file_data)
            
        except Exception as e:
            # 捕获并打印异常，避免程序崩溃
            traceback.print_exception(e)

    def load_annannotation(self, current_id, file_data):
        """
        加载标注数据到图形视图
        
        Args:
            current_id: 当前行ID
            file_data: 包含标注数据的文件信息
        """
        annotation = file_data.get("annotation", [])
        for region in annotation:
            shape_attrs = region.get("shape_attributes", {})
            region_attrs = region.get("region_attributes", {})
            shape_name = shape_attrs.get("name", "rect")
            obj = region_attrs.get("obj", "")
            obj = region_attrs.get("obj", "")
            
            # 根据形状类型添加标注
            if shape_name == "rect":
                self.graphicsView_image_display.add_rect(obj, shape_attrs)
            elif shape_name == "polygon":
                # 检查点数据是否完整
                if "all_points_x" in shape_attrs and "all_points_y" in shape_attrs:
                    self.graphicsView_image_display.add_polygon(obj, shape_attrs)
            elif shape_name == "quadrilateral":
                if "all_points_x" in shape_attrs and "all_points_y" in shape_attrs:
                    self.graphicsView_image_display.add_quadrilateral(obj, shape_attrs)

    def update_file_ui_status(self, current_id, file_data):
        """
        更新文件状态UI显示（标注状态和审核状态）
        
        Args:
            current_id: 当前行ID
            file_data: 文件信息
        """
        # 更新标注状态
        status = file_data.get("status", "")
        annon_status = AnnotationStatus.get_symbol_by_desc(status)
        self.tableWidget_files.setItem(current_id, 1, QTableWidgetItem(annon_status))
        
        # 更新审核状态
        reviews = file_data.get("reviews", {})
        reviews_status = ""
        if len(reviews.get("regions", [])) > 0:
            review = reviews["regions"][0]  # 取第一个评论
            reviews_status = CommentStatus.get_symbol_by_desc(review)
        self.tableWidget_files.setItem(current_id, 2, QTableWidgetItem(reviews_status))

    def keyPressEvent(self, event):
        """处理键盘事件，忽略Esc键避免意外关闭"""
        if event.key() == Qt.Key_Escape:
            event.ignore()  # 忽略Esc键
            return
        super().keyPressEvent(event)